﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using System.Web;

namespace PushService
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in code, svc and config file together.
    // NOTE: In order to launch WCF Test Client for testing this service, please select Service1.svc or Service1.svc.cs at the Solution Explorer and start debugging.
    public class PushS : IPushS
    {
        static Dictionary<string, ClientDetails> clientMap = new Dictionary<string, ClientDetails>();
        #region static List<string> nseSymbolList = new List<string>()
        static List<string> nseSymbolList = new List<string>() 
        { 
            "ABB.NS", 
            "ABIRLANUV.NS", 
            "ACC.NS", 
            "ADANIENT.NS", 
            "ALOKTEXT.NS", 
            "AMTEKAUTO.NS", 
            "APOLLOTYR.NS", 
            "ASHOKLEY.NS", 
            "ASIANPAIN.NS", 
            "BAJAJ-AUT.NS", 
            "BAJAJHIND.NS", 
            "BANKBAROD.NS", 
            "BANKINDIA.NS", 
            "BBTC.NS", 
            "BEL.NS", 
            "BHARATFOR.NS", 
            "BHEL.NS", 
            "BHUSANSTL.NS", 
            "BRITANNIA.NS", 
            "CADILAHC.NS", 
            "CAIRN.NS", 
            "CANBK.NS", 
            "CESC.NS", 
            "CHAMBLFER.NS", 
            "CHENNPETR.NS", 
            "CIPLA.NS", 
            "CROMPGREA.NS", 
            "DABUR.NS", 
            "DCMSRMCON.NS", 
            "DLF.NS", 
            "DRREDDY.NS", 
            "EICHERMOT.NS", 
            "EIDPARRY.NS", 
            "EXIDEIND.NS", 
            "GAIL.NS", 
            "GAMMONIND.NS", 
            "GITANJALI.NS", 
            "GODREJIND.NS", 
            "GRASIM.NS", 
            "GSFC.NS", 
            "HAVELLS.NS", 
            "HCL-INSYS.NS", 
            "HCLTECH.NS", 
            "HDFC.NS", 
            "HEROHONDA.NS", 
            "HINDZINC.NS", 
            "ICICIBANK.NS", 
            "IDBI.NS", 
            "IDEA.NS", 
            "ITC.NS", 
            "IVRCLINFR.NS", 
            "JBFIND.NS", 
            "JINDALSTE.NS", 
            "JPASSOCIA.NS", 
            "JSWSTEEL.NS", 
            "KALPATPOW.NS", 
            "KOTAKBANK.NS", 
            "LT.NS", 
            "LUPIN.NS", 
            "M&M.NS", 
            "MARUTI.NS", 
            "MAX.NS", 
            "MCDOWELL-.NS", 
            "MOTHERSUM.NS", 
            "MPHASIS.NS", 
            "MRF.NS", 
            "NATIONALU.NS", 
            "NFL.NS", 
            "NMDC.NS", 
            "PANTALOON.NS", 
            "PETRONET.NS", 
            "PNB.NS", 
            "POWERGRID.NS", 
            "PRETAILDV.NS", 
            "PTC.NS", 
            "PUNJLLOYD.NS", 
            "RAJESHEXP.NS", 
            "RCOM.NS", 
            "REDINGTON.NS", 
            "REIAGROLT.NS", 
            "RELCAPITA.NS", 
            "RUCHISOYA.NS", 
            "SAIL.NS", 
            "SATYAMCOM.NS", 
            "SESAGOA.NS", 
            "SIEMENS.NS", 
            "SIMPLEXIN.NS", 
            "SKUMARSYN.NS", 
            "STCINDIA.NS", 
            "STER.NS", 
            "SUJANAUNI.NS", 
            "SUNCLAYTO.NS", 
            "SUNPHARMA.NS", 
            "SURANACOR.NS", 
            "TATACHEM.NS", 
            "TCS.NS", 
            "TECHM.NS", 
            "THERMAX.NS", 
            "TORNTPOWE.NS", 
            "TUBEINVES.NS", 
            "TVSMOTOR.NS", 
            "ULTRACEMC.NS", 
            "UNIONBANK.NS", 
            "UNIPHOS.NS", 
            "UTTAMSTL.NS", 
            "VOLTAS.NS", 
            "WIPRO.NS", 
        };
        #endregion static List<string> nseSymbolList = new List<string>()

        static List<string> stockQueryFragmentList = new List<string>();
        static bool stopProcessing;
        public void RegisterChannelUri(string clientSecret,
                                       string sid,
                                       string channelUri,
                                       string clientFriendlyName)
        {
            try
            {
                if (!clientMap.ContainsKey(clientSecret))
                {
                    ClientDetails clientDetails = new ClientDetails(clientSecret, sid, channelUri, clientFriendlyName);
                    clientMap.Add(clientSecret, clientDetails);
                }
                else
                {
                    ClientDetails clientDetails = clientMap[clientSecret];
                    if (!clientDetails.ChannelUriQueue.Contains(channelUri))
                    {
                        if (clientDetails.ChannelUriQueue.Count >= 10)
                        {
                            clientDetails.ChannelUriQueue.Dequeue();
                        }

                        clientDetails.ChannelUriQueue.Enqueue(channelUri);
                    }
                }
            }
            catch
            {
                // need to do something here. for now just an empty catch block to avoid service crash 
            }
        }



        public void SendToastNotification(string text1, string text2)
        {
            string xml = "<toast><visual><binding template=\"ToastText02\"><text id=\"1\">" + text1 + "</text><text id=\"2\">" + text2 + "</text></binding></visual></toast>";
            SendNotification(xml, "wns/toast");
        }

        public void SendTileNotification(string text1, string text2, string text3, string text4,
                                         string text5, string text6, string text7, string text8, string text9)
        {
            string xml =
                "<tile><visual><binding template=\"TileWideText07\"><text id=\"1\">" + text1 +
                "</text><text id=\"2\">" + text2 + "</text><text id=\"3\">" + text3 +
                "</text><text id=\"4\">" + text4 + "</text><text id=\"5\">" + text5 +
                "</text><text id=\"6\">" + text6 + "</text><text id=\"7\">" + text7 +
                "</text><text id=\"8\">" + text8 + "</text><text id=\"9\">" + text9 + 
                "</text></binding></visual></tile>";

            SendNotification(xml, "wns/tile");
        }

        public void ClearClients()
        {
            clientMap.Clear();
        }

        public List<string> GetClients()
        {
            return clientMap.Values.Select(t => t.ClientFriendlyName).ToList();
        }


        public void StartNSETickler()
        {
           stopProcessing = false;
           if(!stopProcessing)
           {
               SendTileNotification("NSE Tickler Starting...", string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, string.Empty);
               //while (true)
               //{
               GetNSEStockDetails();
               //}
           }
           else
           {
               SendTileNotification("NSE Tickler Stopped", string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, string.Empty);
               return;
           }
        }

        public void StopNSETickler()
        {
            stopProcessing = true;
        }

        public string SendSingleStockDetails(string yahooSymbol)
        {
            if (!nseSymbolList.Exists(t => t.ToLower().Trim().Equals(yahooSymbol.ToLower().Trim())))
            {
                return string.Format("Stock symbol {0} could not be found.", yahooSymbol);
            }
            else
            {
                try
                {
                    using (WebClient web = new WebClient())
                    {
                        string csvData = web.DownloadString("http://finance.yahoo.com/d/quotes.csv?s=" + yahooSymbol + "&f=snbaopl1");
                        List<Price> priceDetailList = Parse(csvData);

                        foreach (Price priceDetails in priceDetailList)
                        {
                            string text1 = priceDetails.Name.Replace("\"", "") + ", " + priceDetails.Symbol.Replace("\"", "");
                            string text2 = string.Format("Bid: {0},  Ask: {1}", priceDetails.Bid, priceDetails.Ask);
                            SendToastNotification(text1, text2);
                        }
                    }

                    return string.Format("Quote details for {0} sent to clients.", yahooSymbol);
                }
                catch
                {
                    return string.Format("Error getting stock quote for {0}.", yahooSymbol);
                }
            }
        }

        private void GetNSEStockDetails()
        {
           
            if (stockQueryFragmentList.Count == 0)
            {
                List<string> temp = new List<string>();
                for (int i = 1; i <= nseSymbolList.Count; i++)
                {
                    if (i % 5 == 0)
                    {
                        stockQueryFragmentList.Add(string.Join("+", temp.ToArray()));
                        temp.Clear();
                    }
                    temp.Add(nseSymbolList[i - 1]);
                }

                if (temp.Count > 0)
                {
                    stockQueryFragmentList.Add(string.Join("+", temp.ToArray()));
                    temp.Clear();
                }
            }

            List<Price> stockPriceList = new List<Price>();
            foreach (string queryFragment in stockQueryFragmentList)
            {
                try
                {
                    if (!stopProcessing)
                    {
                        using (WebClient web = new WebClient())
                        {
                            string csvData = web.DownloadString("http://finance.yahoo.com/d/quotes.csv?s=" + queryFragment + "&f=snbaopl1");
                            stockPriceList.AddRange(Parse(csvData));
                            foreach (Price priceDetails in stockPriceList)
                            {
                                if (!stopProcessing)
                                {
                                    string text1 = priceDetails.Name.Replace("\"", "") + ", " + priceDetails.Symbol.Replace("\"", "");
                                    string text2 = "Bid";
                                    string text3 = priceDetails.Bid;
                                    string text4 = "Ask";
                                    string text5 = priceDetails.Ask;
                                    string text6 = "Last";
                                    string text7 = priceDetails.Last;
                                    string text8 = "Previous close";
                                    string text9 = priceDetails.PreviousClose;
                                    
                                    SendTileNotification(text1, text2, text3, text4, text5, text6, text7, text8, text9);
                                }
                                else
                                {
                                    SendTileNotification("NSE Tickler Stopped", string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, string.Empty);
                                    return;
                                }
                            }
                            stockPriceList.Clear();
                        }
                    }
                    else
                    {
                        SendTileNotification("NSE Tickler Stopped", string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, string.Empty);
                        return;
                    }
                }
                catch
                {
                    // what do we do? i guess nothing. we will need to do something after the foreach mainly
                }
            }
        }

        private List<Price> Parse(string csvData)
        {
            List<Price> prices = new List<Price>();

            string[] rows = csvData.Replace("\r", "").Split('\n');

            foreach (string row in rows)
            {
                if (string.IsNullOrEmpty(row)) continue;

                string[] cols = row.Split(',');

                Price p = new Price();

                p.Symbol = cols[0];

                p.Name = cols[1];

                try
                {
                    p.Bid = Convert.ToDecimal(cols[2]).ToString();
                }
                catch
                {
                    p.Bid = "N/A";
                }

                try
                {
                    p.Ask = Convert.ToDecimal(cols[3]).ToString();
                }
                catch
                {
                    p.Ask = "N/A";
                }

                try
                {
                    p.Open = Convert.ToDecimal(cols[4]).ToString();
                }
                catch
                {
                    p.Open = "N/A";
                }

                try
                {
                    p.PreviousClose = Convert.ToDecimal(cols[5]).ToString();
                }
                catch
                {
                    p.PreviousClose = "N/A";
                }

                try
                {
                    p.Last = Convert.ToDecimal(cols[6]).ToString();
                }
                catch
                {
                    p.Last = "N/A";
                }

                prices.Add(p);
            }

            return prices;
        }

        private void SendNotification(string xml, string wnsType)
        {
            try
            {
                foreach (ClientDetails clientDetails in clientMap.Values)
                {

                    OAuthToken token = GetAccessToken(clientDetails.ClientSecret,
                                                       clientDetails.SID);

                    foreach (string channelUri in clientDetails.ChannelUriQueue)
                    {
                        HttpWebRequest request = HttpWebRequest.Create(channelUri) as HttpWebRequest;
                        request.Method = "POST";

                        request.Headers.Add("X-WNS-Type", wnsType);
                        request.Headers.Add("Authorization", String.Format("Bearer {0}", token.AccessToken));

                        byte[] contentInBytes = Encoding.UTF8.GetBytes(xml);

                        using (Stream requestStream = request.GetRequestStream())
                        {
                            requestStream.Write(contentInBytes, 0, contentInBytes.Length);
                        }

                        using (HttpWebResponse webResponse = (HttpWebResponse)request.GetResponse())
                        {
                            string code = webResponse.StatusCode.ToString();
                        }
                    }
                }
            }
            catch
            {
                // need to do something here. for now just an empty catch block to avoid service crash 
            }
        }
        
        private OAuthToken GetOAuthTokenFromJson(string jsonString)
        {
            using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(jsonString)))
            {
                var ser = new DataContractJsonSerializer(typeof(OAuthToken));
                var oAuthToken = (OAuthToken)ser.ReadObject(ms);
                return oAuthToken;
            }
        }

        private OAuthToken GetAccessToken(string secret, string sid)
        {
            var urlEncodedSecret = HttpUtility.UrlEncode(secret);
            var urlEncodedSid = HttpUtility.UrlEncode(sid);

            var body =
              String.Format("grant_type=client_credentials&client_id={0}&client_secret={1}&scope=notify.windows.com", urlEncodedSid, urlEncodedSecret);

            string response;
            using (var client = new WebClient())
            {
                client.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
                response = client.UploadString("https://login.live.com/accesstoken.srf", body);
            }
            return GetOAuthTokenFromJson(response);
        }


    }

    class ClientDetails
    {
        private string _clientSecret;

        public string ClientSecret
        {
            get { return _clientSecret; }
        }

        private string _sid;

        public string SID
        {
            get { return _sid; }
        }

        private Queue<string> _channelUriQueue = new Queue<string>();

        public Queue<string> ChannelUriQueue
        {
            get { return _channelUriQueue; }
            set { _channelUriQueue = value; }
        }

        private string _clientFriendlyName;

        public string ClientFriendlyName
        {
            get { return _clientFriendlyName; }
        }

        public ClientDetails(string clientSecret,
                             string sid,
                             string channelUri, 
                             string clientFriendlyName)
        {
            this._clientSecret = clientSecret;
            this._sid = sid;
            this._channelUriQueue.Enqueue(channelUri);
            this._clientFriendlyName = clientFriendlyName;
        }

    }

    class Price
    {
        public string Symbol { get; set; }
        public string Name { get; set; }
        public string Bid { get; set; }
        public string Ask { get; set; }
        public string Open { get; set; }
        public string PreviousClose { get; set; }
        public string Last { get; set; }
    }
}
